package exteditapp.command;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;

import exteditapp.FileDefault;
import exteditapp.state.StateError;

/**
 * Tries to unlock file from editing on Plone file system.
 * @author Arturas Smorgun, arturas@smorgun.lt
 */
public class CommandUnlock extends ICommand  {

	/**
	 * Default constructor
	 */
	public CommandUnlock(FileDefault file){
		super(file);
	}

	/**
	 * Execution of unlock procedure
	 * @return true on success and false otherwise
	 */
	public Boolean execute() {
		
		/**
		 * Begin of execution.
		 */
		
		execution = true;
		
		/**
		 * Do unlock.
		 */
		
		int retryNumber = 0; //used in case of network timeout
		try{
			URL myCheckUrl = new URL(file.getDownloadURL()+file.getModule().URL_DO_UNLOCK);
			HttpURLConnection connection = (HttpURLConnection) myCheckUrl.openConnection();
			connection.setConnectTimeout(3000);
			connection.setReadTimeout(3000);
			String lockPath = file.getModule().getSettings().getParameter("lockPath");
			String lockToken = file.getModule().getSettings().getParameter("lockToken");
			connection.setRequestProperty("If", "<"+lockPath+"> (<"+lockToken+">)");
			BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			String locked = br.readLine();
			connection.disconnect();
			//executed if file locking is failed
			if(locked.compareTo("1") != 0){
				StateError newState = new StateError(file.getCurrentState().getIterator(), this.file);
				newState.setLabel("ERROR: FAILED TO UNLOCK");
				this.file.setCurrentState(newState);
				execution = false;
				file.getModule().getInvoker().setExecution(execution);
				return false;
			}
			
		}

		/**
		 * Catches passed invalid URL string.
		 */
		
		catch(MalformedURLException e){
			e.printStackTrace();
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("FATAL SYSTEM ERROR");
			this.file.setCurrentState(newState);
			return false;
		}
		
		/**
		 * Catches lost request, request rejection or server response error.
		 */
		
		catch(ConnectException e){
			e.printStackTrace();
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("SERVER NOT FOUND");
			return false;
		}
		
		/**
		 * Catches network timeout - tries to retry request 5 times before showing error message.
		 */
		
		catch(SocketTimeoutException e){
			e.printStackTrace();
			retryNumber++;
			if(retryNumber > 5){
				StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
				newState.setup("CONNECTION TIMEOUT");
				this.file.setCurrentState(newState);
				return false;
			}
		}
		
		/**
		 * Catches IO exceptions (usually - everything what related with connection errors).
		 */
		
		catch(IOException e){
			e.printStackTrace();
			StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
			newState.setup("CONNECTION ERROR");
			this.file.setCurrentState(newState);
			return false;
		}
		
		/**
		 * Unlock accomplished successfully - change current state.
		 * Change state into state 
		 */
		
		StateError newState = new StateError(this.file.getCurrentState().getIterator(), this.file);
		newState.setup("YOU CAN CLOSE APPLICATION NOW", null, 5);
//		StateEnding newState = new StateEnding(file.getCurrentState().getIterator(), this.file);
		this.file.setCurrentState(newState);
		
		/**
		 * End of execution.
		 * Mark in Execution invoker, that this thread is almost ended it's workand next execu-
		 * tion (from queue) can be executed.
		 */
		
		System.out.println("EVERYTHING IS OK");
		execution = false;
		file.getModule().getInvoker().setExecution(execution);
		return true;
	}

	/**
	 * Method for command execution invocation in separated thread
	 */
	public void run() {
		execute();
	}
	
}
